/**
 * 对比工具共享组件库
 * 包含光标管理、差异算法、UI工具等通用功能
 */

// ========== 光标管理类 ==========

class CursorManager {
    /**
     * 保存当前光标位置
     * @param {HTMLElement} element - 编辑器元素
     * @returns {Object|null} 光标状态对象，如果没有选区则返回 null
     */
    static saveCursorPosition(element) {
        const selection = window.getSelection();
        if (!selection.rangeCount || !element.contains(selection.anchorNode)) {
            return null;
        }
        
        const range = selection.getRangeAt(0);
        const { lineIndex, charOffset } = this.getLineAndOffset(element, range.startContainer, range.startOffset);
        
        return {
            element: element,
            lineIndex: lineIndex,
            charOffset: charOffset,
            isAtEnd: false,
            timestamp: Date.now()
        };
    }

    /**
     * 恢复光标位置
     * @param {HTMLElement} element - 编辑器元素
     * @param {Object} cursorState - 光标状态对象
     */
    static restoreCursorPosition(element, cursorState) {
        if (!cursorState || cursorState.element !== element) {
            return;
        }
        
        // 检查元素是否仍然有焦点
        if (document.activeElement !== element && !element.contains(document.activeElement)) {
            return;
        }
        
        this.setCaretPosition(element, cursorState.lineIndex, cursorState.charOffset);
    }

    /**
     * 从 DOM 节点计算行索引和字符偏移
     * @param {HTMLElement} element - 编辑器元素
     * @param {Node} node - 当前节点
     * @param {number} offset - 节点内的偏移量
     * @returns {Object} { lineIndex, charOffset }
     */
    static getLineAndOffset(element, node, offset) {
        // 如果内容为空
        if (!element.querySelector('.code-line')) {
            return { lineIndex: 0, charOffset: 0 };
        }
        
        // 找到包含当前节点的 code-line
        let currentNode = node;
        while (currentNode && currentNode !== element) {
            if (currentNode.classList && currentNode.classList.contains('code-line')) {
                break;
            }
            currentNode = currentNode.parentNode;
        }
        
        if (!currentNode || currentNode === element) {
            return { lineIndex: 0, charOffset: 0 };
        }
        
        // 获取行索引
        const codeLines = Array.from(element.querySelectorAll('.code-line'));
        const lineIndex = codeLines.indexOf(currentNode);
        
        if (lineIndex === -1) {
            return { lineIndex: 0, charOffset: 0 };
        }
        
        // 计算字符偏移
        const lineContent = currentNode.querySelector('.line-content');
        if (!lineContent) {
            return { lineIndex, charOffset: 0 };
        }
        
        let charOffset = 0;
        const walker = document.createTreeWalker(
            lineContent,
            NodeFilter.SHOW_TEXT,
            null,
            false
        );
        
        let currentTextNode = walker.nextNode();
        while (currentTextNode) {
            if (currentTextNode === node) {
                charOffset += offset;
                break;
            }
            charOffset += currentTextNode.textContent.length;
            currentTextNode = walker.nextNode();
        }
        
        return { lineIndex, charOffset };
    }

    /**
     * 设置光标位置
     * @param {HTMLElement} element - 编辑器元素
     * @param {number} lineIndex - 行索引（0-based）
     * @param {number} charOffset - 字符偏移量
     */
    static setCaretPosition(element, lineIndex, charOffset) {
        const codeLines = element.querySelectorAll('.code-line');
        
        // 边缘情况：如果没有行，将光标放在 element 本身
        if (codeLines.length === 0) {
            try {
                const range = document.createRange();
                const selection = window.getSelection();
                range.selectNodeContents(element);
                range.collapse(false);
                selection.removeAllRanges();
                selection.addRange(range);
            } catch (e) {
                console.warn('Failed to set cursor in empty view:', e);
            }
            return;
        }
        
        // 边缘情况：确保行索引在有效范围内（行数变化时）
        const targetLineIndex = Math.max(0, Math.min(lineIndex, codeLines.length - 1));
        const targetLine = codeLines[targetLineIndex];
        const lineContent = targetLine.querySelector('.line-content');
        
        if (!lineContent) {
            return;
        }
        
        // 遍历文本节点找到正确的位置
        const walker = document.createTreeWalker(
            lineContent,
            NodeFilter.SHOW_TEXT,
            null,
            false
        );
        
        let currentOffset = 0;
        let targetNode = null;
        let targetOffset = 0;
        
        let textNode = walker.nextNode();
        while (textNode) {
            const nodeLength = textNode.textContent.length;
            if (currentOffset + nodeLength >= charOffset) {
                targetNode = textNode;
                targetOffset = charOffset - currentOffset;
                break;
            }
            currentOffset += nodeLength;
            textNode = walker.nextNode();
        }
        
        // 边缘情况：如果没有找到文本节点或字符偏移超出，放在行内容的末尾
        if (!targetNode) {
            // 尝试获取最后一个文本节点
            const lastWalker = document.createTreeWalker(
                lineContent,
                NodeFilter.SHOW_TEXT,
                null,
                false
            );
            let lastTextNode = null;
            let node = lastWalker.nextNode();
            while (node) {
                lastTextNode = node;
                node = lastWalker.nextNode();
            }
            
            if (lastTextNode) {
                targetNode = lastTextNode;
                targetOffset = lastTextNode.textContent.length;
            } else {
                // 如果没有文本节点，放在 lineContent 本身
                targetNode = lineContent;
                targetOffset = 0;
            }
        }
        
        // 设置选区
        try {
            const range = document.createRange();
            const selection = window.getSelection();
            
            // 边缘情况：确保偏移量不超过节点长度
            const safeOffset = targetNode.nodeType === Node.TEXT_NODE 
                ? Math.min(targetOffset, targetNode.textContent.length)
                : 0;
                
            range.setStart(targetNode, safeOffset);
            range.collapse(true);
            selection.removeAllRanges();
            selection.addRange(range);
        } catch (e) {
            // 如果设置失败，尝试放在末尾作为后备方案
            try {
                const range = document.createRange();
                const selection = window.getSelection();
                range.selectNodeContents(lineContent);
                range.collapse(false);
                selection.removeAllRanges();
                selection.addRange(range);
            } catch (fallbackError) {
                console.warn('Failed to restore cursor position:', e, fallbackError);
            }
        }
    }
}

// ========== 差异算法类 ==========

class DiffEngine {
    /**
     * 计算行级差异
     * @param {string} text1 - 第一段文本
     * @param {string} text2 - 第二段文本
     * @returns {Object} 差异结果
     */
    static computeLineDiff(text1, text2) {
        const lines1 = text1 ? text1.split('\n') : [''];
        const lines2 = text2 ? text2.split('\n') : [''];
        const maxLines = Math.max(lines1.length, lines2.length);
        
        const diffs = [];
        
        for (let i = 0; i < maxLines; i++) {
            const line1 = lines1[i] !== undefined ? lines1[i] : '';
            const line2 = lines2[i] !== undefined ? lines2[i] : '';
            
            if (line1 === line2) {
                diffs.push({
                    type: 'equal',
                    lineIndex: i,
                    content1: line1,
                    content2: line2
                });
            } else {
                diffs.push({
                    type: 'modified',
                    lineIndex: i,
                    content1: line1,
                    content2: line2,
                    charDiff: this.computeCharDiff(line1, line2)
                });
            }
        }
        
        return {
            lines1: lines1,
            lines2: lines2,
            diffs: diffs
        };
    }

    /**
     * 计算字符级差异
     * @param {string} str1 - 第一个字符串
     * @param {string} str2 - 第二个字符串
     * @returns {Object} 字符级差异结果
     */
    static computeCharDiff(str1, str2) {
        const m = str1.length;
        const n = str2.length;
        const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
        
        for (let i = 1; i <= m; i++) {
            for (let j = 1; j <= n; j++) {
                if (str1[i - 1] === str2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        
        let html1 = '';
        let html2 = '';
        let i = m, j = n;
        
        const ops = [];
        while (i > 0 || j > 0) {
            if (i > 0 && j > 0 && str1[i - 1] === str2[j - 1]) {
                ops.unshift({ type: 'equal', char1: str1[i - 1], char2: str2[j - 1] });
                i--;
                j--;
            } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
                ops.unshift({ type: 'insert', char2: str2[j - 1] });
                j--;
            } else if (i > 0) {
                ops.unshift({ type: 'delete', char1: str1[i - 1] });
                i--;
            }
        }
        
        for (const op of ops) {
            if (op.type === 'equal') {
                html1 += UIUtils.escapeHtml(op.char1);
                html2 += UIUtils.escapeHtml(op.char2);
            } else if (op.type === 'delete') {
                html1 += `<span class="diff-char">${UIUtils.escapeHtml(op.char1)}</span>`;
            } else if (op.type === 'insert') {
                html2 += `<span class="diff-char">${UIUtils.escapeHtml(op.char2)}</span>`;
            }
        }
        
        return { html1, html2, operations: ops };
    }

    /**
     * 计算 JSON 语义差异
     * @param {Object} obj1 - 第一个 JSON 对象
     * @param {Object} obj2 - 第二个 JSON 对象
     * @param {string} path - 当前路径
     * @returns {Array} JSON 差异数组
     */
    static computeJsonDiff(obj1, obj2, path = '') {
        const diffs = [];
        
        // 获取所有键
        const keys1 = obj1 && typeof obj1 === 'object' ? Object.keys(obj1) : [];
        const keys2 = obj2 && typeof obj2 === 'object' ? Object.keys(obj2) : [];
        const allKeys = new Set([...keys1, ...keys2]);
        
        for (const key of allKeys) {
            const currentPath = path ? `${path}.${key}` : key;
            const val1 = obj1 && typeof obj1 === 'object' ? obj1[key] : undefined;
            const val2 = obj2 && typeof obj2 === 'object' ? obj2[key] : undefined;
            
            if (val1 === undefined) {
                diffs.push({
                    type: 'added',
                    path: currentPath,
                    newValue: val2,
                    valueType: this.getValueType(val2)
                });
            } else if (val2 === undefined) {
                diffs.push({
                    type: 'removed',
                    path: currentPath,
                    oldValue: val1,
                    valueType: this.getValueType(val1)
                });
            } else if (this.isObject(val1) && this.isObject(val2)) {
                // 递归比较对象
                diffs.push(...this.computeJsonDiff(val1, val2, currentPath));
            } else if (val1 !== val2) {
                diffs.push({
                    type: 'modified',
                    path: currentPath,
                    oldValue: val1,
                    newValue: val2,
                    oldValueType: this.getValueType(val1),
                    newValueType: this.getValueType(val2)
                });
            }
        }
        
        return diffs;
    }

    /**
     * 判断是否为对象
     * @param {*} value - 要判断的值
     * @returns {boolean}
     */
    static isObject(value) {
        return value !== null && typeof value === 'object' && !Array.isArray(value);
    }

    /**
     * 获取值的类型
     * @param {*} value - 要判断的值
     * @returns {string}
     */
    static getValueType(value) {
        if (value === null) return 'null';
        if (Array.isArray(value)) return 'array';
        return typeof value;
    }
}

// ========== UI 工具类 ==========

class UIUtils {
    /**
     * HTML 转义
     * @param {string} text - 要转义的文本
     * @returns {string} 转义后的文本
     */
    static escapeHtml(text) {
        return text
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#039;');
    }

    /**
     * 创建代码行
     * @param {number} lineNum - 行号
     * @param {string} content - 行内容
     * @param {boolean} isDiff - 是否为差异行
     * @returns {string} HTML 字符串
     */
    static createCodeLine(lineNum, content, isDiff = false) {
        const diffClass = isDiff ? ' diff-line' : '';
        return `<div class="code-line${diffClass}">
            <div class="line-number">${lineNum}</div>
            <div class="line-content">${content || '&nbsp;'}</div>
        </div>`;
    }

    /**
     * 绘制连接线
     * @param {HTMLCanvasElement} canvas - 画布元素
     * @param {HTMLElement} panel1 - 左侧面板
     * @param {HTMLElement} panel2 - 右侧面板
     */
    static drawConnections(canvas, panel1, panel2) {
        if (!canvas || !panel1 || !panel2) return;
        
        const ctx = canvas.getContext('2d');
        canvas.width = canvas.offsetWidth;
        canvas.height = canvas.offsetHeight;
        
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        
        const diffLines1 = panel1.querySelectorAll('.code-line.diff-line');
        const diffLines2 = panel2.querySelectorAll('.code-line.diff-line');
        
        ctx.strokeStyle = 'rgba(231, 76, 60, 0.2)';
        ctx.lineWidth = 1;
        
        for (let i = 0; i < Math.min(diffLines1.length, diffLines2.length); i++) {
            const line1 = diffLines1[i];
            const line2 = diffLines2[i];
            
            const rect1 = line1.getBoundingClientRect();
            const rect2 = line2.getBoundingClientRect();
            const canvasRect = canvas.getBoundingClientRect();
            
            const x1 = rect1.right - canvasRect.left;
            const y1 = rect1.top + rect1.height / 2 - canvasRect.top;
            const x2 = rect2.left - canvasRect.left;
            const y2 = rect2.top + rect2.height / 2 - canvasRect.top;
            
            ctx.beginPath();
            ctx.moveTo(x1, y1);
            ctx.lineTo(x2, y2);
            ctx.stroke();
        }
    }

    /**
     * 获取文本内容
     * @param {HTMLElement} element - 元素
     * @returns {string} 文本内容
     */
    static getTextContent(element) {
        return element.innerText || element.textContent || '';
    }

    /**
     * 验证 JSON 格式
     * @param {string} text - 要验证的文本
     * @returns {boolean} 是否为有效 JSON
     */
    static isValidJSON(text) {
        try {
            JSON.parse(text);
            return true;
        } catch {
            return false;
        }
    }

    /**
     * 格式化 JSON
     * @param {string} text - JSON 文本
     * @param {number} indent - 缩进空格数
     * @returns {string} 格式化后的 JSON
     */
    static formatJSON(text, indent = 2) {
        try {
            return JSON.stringify(JSON.parse(text), null, indent);
        } catch (e) {
            throw new Error('Invalid JSON format: ' + e.message);
        }
    }

    /**
     * 防抖函数
     * @param {Function} func - 要防抖的函数
     * @param {number} delay - 延迟时间（毫秒）
     * @returns {Function} 防抖后的函数
     */
    static debounce(func, delay) {
        let timeoutId;
        return function (...args) {
            clearTimeout(timeoutId);
            timeoutId = setTimeout(() => func.apply(this, args), delay);
        };
    }

    /**
     * 复制文本到剪贴板
     * @param {string} text - 要复制的文本
     * @returns {Promise<boolean>} 是否复制成功
     */
    static async copyToClipboard(text) {
        try {
            if (navigator.clipboard && window.isSecureContext) {
                await navigator.clipboard.writeText(text);
                return true;
            } else {
                // 降级方案
                const textArea = document.createElement('textarea');
                textArea.value = text;
                textArea.style.position = 'fixed';
                textArea.style.left = '-999999px';
                textArea.style.top = '-999999px';
                document.body.appendChild(textArea);
                textArea.focus();
                textArea.select();
                
                const result = document.execCommand('copy');
                document.body.removeChild(textArea);
                return result;
            }
        } catch (error) {
            console.error('Failed to copy text:', error);
            return false;
        }
    }

    /**
     * 显示提示消息
     * @param {string} message - 提示消息
     * @param {string} type - 消息类型 ('success', 'error', 'info')
     * @param {number} duration - 显示时长（毫秒）
     */
    static showToast(message, type = 'info', duration = 3000) {
        // 创建提示元素
        const toast = document.createElement('div');
        toast.className = `toast toast-${type}`;
        toast.textContent = message;
        
        // 添加样式
        Object.assign(toast.style, {
            position: 'fixed',
            top: '20px',
            right: '20px',
            padding: '12px 20px',
            borderRadius: '6px',
            color: 'white',
            fontSize: '14px',
            fontWeight: '500',
            zIndex: '10000',
            opacity: '0',
            transform: 'translateY(-20px)',
            transition: 'all 0.3s ease'
        });
        
        // 设置背景色
        const colors = {
            success: '#28a745',
            error: '#dc3545',
            info: '#007bff',
            warning: '#ffc107'
        };
        toast.style.backgroundColor = colors[type] || colors.info;
        
        // 添加到页面
        document.body.appendChild(toast);
        
        // 显示动画
        setTimeout(() => {
            toast.style.opacity = '1';
            toast.style.transform = 'translateY(0)';
        }, 10);
        
        // 自动隐藏
        setTimeout(() => {
            toast.style.opacity = '0';
            toast.style.transform = 'translateY(-20px)';
            setTimeout(() => {
                if (document.body.contains(toast)) {
                    document.body.removeChild(toast);
                }
            }, 300);
        }, duration);
    }
}

// ========== 事件管理类 ==========

class EventManager {
    constructor() {
        this.listeners = new Map();
    }

    /**
     * 添加事件监听器
     * @param {string} event - 事件名称
     * @param {Function} callback - 回调函数
     */
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
    }

    /**
     * 移除事件监听器
     * @param {string} event - 事件名称
     * @param {Function} callback - 回调函数
     */
    off(event, callback) {
        if (this.listeners.has(event)) {
            const callbacks = this.listeners.get(event);
            const index = callbacks.indexOf(callback);
            if (index > -1) {
                callbacks.splice(index, 1);
            }
        }
    }

    /**
     * 触发事件
     * @param {string} event - 事件名称
     * @param {...*} args - 事件参数
     */
    emit(event, ...args) {
        if (this.listeners.has(event)) {
            this.listeners.get(event).forEach(callback => {
                try {
                    callback(...args);
                } catch (e) {
                    console.error('Event callback error:', e);
                }
            });
        }
    }
}

// 导出类（如果在模块环境中）
if (typeof module !== 'undefined' && module.exports) {
    module.exports = {
        CursorManager,
        DiffEngine,
        UIUtils,
        EventManager
    };
}